﻿using Fast.ConfigureOptions;
using Fast.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.Options;
using StackExchange.Profiling;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading;

namespace Fast
{
    [SkipScan]
    public static class App
    {
        /// <summary>
        /// 私有设置，避免重复解析
        /// </summary>
        private static AppSettingsOptions _settings;

        /// <summary>
        /// 应用全局配置
        /// </summary>
        public static AppSettingsOptions Settings => _settings ??= GetConfig<AppSettingsOptions>("AppSettings", true);

        /// <summary>
        /// 应用程序有效程序集
        /// </summary>
        public static IEnumerable<Assembly> Assemblies { get; }

        /// <summary>
        /// 外部程序集
        /// </summary>
        public static IEnumerable<Assembly> ExternalAssemblies;

        /// <summary>
        /// 有效程序集中的类型
        /// </summary>
        public static IEnumerable<Type> Types { get; }

        /// <summary>
        /// 全局配置选项
        /// </summary>
        public static IConfiguration Configuration => InternalApp.Services.GetConfiguration();

        /// <summary>
        /// 根服务
        /// </summary>
        public static IServiceProvider ServicesProvider { get; internal set; }

        /// <summary>
        /// Http请求上下文
        /// </summary>
        public static HttpContext HttpContext => ServicesProvider?.GetService<IHttpContextAccessor>()?.HttpContext;

        /// <summary>
        /// Json序列化对象
        /// </summary>
        public static IJsonSerializer JsonSerializer => GetRequiredService<IJsonSerializer>();

        /// <summary>
        /// 构造函数
        /// </summary>
        static App()
        {
            //加载程序集
            var assembly = GetAssemblies();
            Assemblies = assembly.Assemblies;
            ExternalAssemblies = assembly.ExternalAssemblies;

            //程序集中的有效类型
            Types = Assemblies.SelectMany(a => a.GetTypes().Where(t => t.IsPublic && !t.IsDefined(typeof(SkipScanAttribute), false)));
        }

        public static T GetService<T>()
        {
            if (HttpContext?.RequestServices != null)
                return HttpContext.RequestServices.GetService<T>();
            if (ServicesProvider != null)
                return HttpContext.RequestServices.GetService<T>();

            return InternalApp.Services.BuildServiceProvider().GetService<T>();
        }

        public static T GetRequiredService<T>()
        {
            if (HttpContext?.RequestServices != null)
                return HttpContext.RequestServices.GetRequiredService<T>();
            if (ServicesProvider != null)
                return HttpContext.RequestServices.GetRequiredService<T>();

            return InternalApp.Services.BuildServiceProvider().GetRequiredService<T>();
        }


        /// <summary>
        /// 获取配置
        /// </summary>
        /// <typeparam name="TOptions">强类型选项类</typeparam>
        /// <param name="path">配置中对应的Key</param>
        /// <param name="loadPostConfigure"></param>
        /// <returns>TOptions</returns>
        public static TOptions GetConfig<TOptions>(string path, bool loadPostConfigure = false)
        {
            var options = Configuration.GetSection(path).Get<TOptions>();

            // 加载默认选项配置
            if (loadPostConfigure && typeof(IConfigureOptions).IsAssignableFrom(typeof(TOptions)))
            {
                var postConfigure = typeof(TOptions).GetMethod("PostConfigure");
                if (postConfigure != null)
                {
                    options ??= Activator.CreateInstance<TOptions>();
                    postConfigure.Invoke(options, new object[] { options, Configuration });
                }
            }

            return options;
        }

        /// <summary>
        /// 获取配置
        /// </summary>
        /// <typeparam name="TOptions">强类型选项类</typeparam>
        /// <param name="loadPostConfigure"></param>
        /// <returns>TOptions</returns>
        public static TOptions GetOptions<TOptions>(bool loadPostConfigure = false)
            where TOptions : class
        {
            var options = ServicesProvider.GetService<IOptions<TOptions>>().Value;

            // 加载默认选项配置
            if (loadPostConfigure && typeof(IConfigureOptions).IsAssignableFrom(typeof(TOptions)))
            {
                var postConfigure = typeof(TOptions).GetMethod("PostConfigure");
                if (postConfigure != null)
                {
                    options ??= Activator.CreateInstance<TOptions>();
                    postConfigure.Invoke(options, new object[] { options, Configuration });
                }
            }

            return options;
        }

        /// <summary>
        /// 获取应用有效程序集
        /// </summary>
        /// <returns></returns>
        private static (HashSet<Assembly> Assemblies, HashSet<Assembly> ExternalAssemblies) GetAssemblies()
        {
            // 需排除的程序集后缀
            var excludeAssemblySuffixs = new string[] {
                "Database.Migrations"
            };

            var dependencyContext = DependencyContext.Default;

            var scanAssemblies = dependencyContext.RuntimeLibraries
                .Where(r => r.Type == "project" && !excludeAssemblySuffixs.Any(e => r.Name.EndsWith(e))
                || r.Type == "package" && r.Name.StartsWith("Fast"))
                .Select(r => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(r.Name)))
                .ToHashSet();

            var externalAssemblies = new HashSet<Assembly>();

            // 加载 `appsetting.json` 配置的外部程序集
            if (Settings.ExternalAssemblies != null && Settings.ExternalAssemblies.Any())
            {
                foreach (var externalAssembly in Settings.ExternalAssemblies)
                {
                    // 加载外部程序集
                    var assemblyFileFullPath = Path.Combine(AppContext.BaseDirectory
                        , externalAssembly.EndsWith(".dll") ? externalAssembly : $"{externalAssembly}.dll");

                    // 根据路径加载程序集
                    var loadedAssembly = AssemblyHelper.LoadAssembly(assemblyFileFullPath);
                    if (loadedAssembly == default) continue;

                    // 合并程序集
                    scanAssemblies.Add(loadedAssembly);
                    externalAssemblies.Add(loadedAssembly);
                }
            }

            return (scanAssemblies, externalAssemblies);
        }

        /// <summary>
        /// 打印验证信息到 MiniProfiler
        /// </summary>
        /// <param name="category">分类</param>
        /// <param name="state">状态</param>
        /// <param name="message">消息</param>
        /// <param name="isError">是否为警告消息</param>
        public static void PrintToMiniProfiler(string category, string state, string message = null, bool isError = false)
        {
            if (!_settings.HasInjectMiniProfiler.Value) return;

            // 打印消息
            var titleCaseCategory = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(category);
            var customTiming = MiniProfiler.Current?.CustomTiming(category, string.IsNullOrWhiteSpace(message) ? $"{titleCaseCategory} {state}" : message, state);
            if (customTiming == null) return;

            // 判断是否是警告消息
            if (isError) customTiming.Errored = true;
        }
    }
}
